package com.louis.reflect;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


/**
 * @author XRY
 * @date 2023年06月26日9:02
 */
public class TestCar {
    Logger logger = LoggerFactory.getLogger(TestCar.class);
    //1、获取class对象多种方式
    @Test
    public void testGetClass() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1、类名.class
        Class<Car> clazz01 = Car.class;
        //2、对象.getClass()
        Class<? extends Car> clazz02 = new Car().getClass();
        //3、Class.forName("全路径")
        Class<?> clazz03 = Class.forName("com.louis.reflect.Car");
        //实例化
        Car car = (Car)clazz03.getDeclaredConstructor().newInstance();
        logger.info("car" + car);
        /*[2023-06-26 09:16:13:036] [INFO] - com.louis.reflect.TestCar.testGetClass(TestCar.java:28) - carcom.louis.reflect.Car@2445445a*/
    }
    //2、通过反射获取构造方法
    @Test
    public void testCacheConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Car> clazz = Car.class;
        //获取所有构造
        //getConstructors针对public方法,如果是private则不能够使用这种方法获取，如果构造方法中包含私有的方法，则需要使用getDeclaredConstructors
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            logger.info("constructor" + constructor.getName() + "参数个数" + constructor.getParameterCount());
        /*
        * [2023-06-26 09:57:16:855] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数0
          [2023-06-26 09:57:16:858] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:38) - constructorcom.louis.reflect.Car参数个数3
        * */
        }
        //指定有参数的构造去创建对象
        //1、构造是public,如果目标对象是private则会报错
        /*Constructor<Car> haveParameterPub = clazz.getConstructor(String.class, int.class, String.class);
        Car car = haveParameterPub.newInstance("野马", 1, "blue");
        /*[2023-06-26 10:07:47:947] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0
          [2023-06-26 10:07:47:950] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3
          汽车Car{bind='野马', lifeTime=1, color='blue'}*/
        //2、构造是private
        Constructor<Car> haveParameterPri = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        haveParameterPri.setAccessible(true);//设置访问权限，如果为false不能够访问
        Car car1 = haveParameterPri.newInstance("悍马", 2, "yellow");
        System.out.println("car1 = " + car1);
        /*
        [2023-06-26 10:13:58:492] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数0
        [2023-06-26 10:13:58:496] [INFO] - com.louis.reflect.TestCar.testCacheConstructor(TestCar.java:39) - constructorcom.louis.reflect.Car参数个数3
        car1 = Car{bind='悍马', lifeTime=2, color='yellow'}
        * */
    }

    //3、获取属性
    @Test
    public void getAttribute() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获得类的字节码文件，类、对象和class.forName
        Class<?> carClass = Class.forName("com.louis.reflect.Car");
        //实例化
        Car car = (Car)carClass.getDeclaredConstructor().newInstance();
        //获取其中所有的public方法
        Field[] fields = carClass.getFields();
        //获取所有的属性，包括私有的属性
        Field[] declaredFields = carClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("declaredField.getName() = " + declaredField.getName());
            //给属性赋值
            if(declaredField.getName().equals("bind")){
                //设置允许访问
                declaredField.setAccessible(true);
                //传入对象和属性值
                declaredField.set(car,"野马");
            }
            System.out.println("car" + car);
        }
        /*
        * declaredField.getName() = bind
          carCar{bind='野马', lifeTime=0, color='null'}
          declaredField.getName() = lifeTime
          carCar{bind='野马', lifeTime=0, color='null'}
          declaredField.getName() = color
          carCar{bind='野马', lifeTime=0, color='null'}

        * */
    }

    //4、获取方法
    @Test
    public void getMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Car car = new Car("Benz", 10, "black");
        Class<? extends Car> clazz = car.getClass();
        //1、public方法,不会取到私有
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
//            System.out.println(method.getName());
            //执行方法toString
            if(method.getName().equals("toString")){
                String invoke = (String)method.invoke(car);
                System.out.println("toString执行了" + invoke);
                /*toString执行了Car{bind='Benz', lifeTime=10, color='black'}*/
            }
        }
        //2、private方法
        Method[] methodsAll = clazz.getDeclaredMethods();
        for (Method methodA : methodsAll) {
            //执行私有方法
            if(methodA.getName().equals("use")){
                methodA.setAccessible(true);
                methodA.invoke(car);
            }
        }
        /*私有方法..........*/
    }
}
